#ifndef MROWKOWY_H__
#define MROWKOWY_H__

struct Ant {
	int** solution;		// aktualny przydzial [stud][prze]
	int objective;		// akualna wartosc funkcji szczescia
	int best_objective;	// najlepsza jak do tej pory wartosc funkcji szczescia
	double lpi;			// local progress index -- jak bardzo mrowka ulepszala swoje rozwiazanie
	int* terms;			// wolne miejsca na terminach
};

// czytanie wejscia, w formacie wewnetrznym
// do konwersji sluza ./pref-out2int.perl ./term-out2int.perl
void read_input();

// FUNKCJE WYKORZYSTYWANE PRZEZ MROWKOWY:

// INICJALIZACJA -- ustawia odpowiednie wartosci w rozwiazaniu, usuwajac
// z przydzialu studentow, ktorzy nie zapisuja sie na jakies przedmioty
void solution_trim(int** solution);

// INICJALIZACJA -- zupelnie losowe rozwiazanie, zachowujace jednak
// przestrzeganie liczby studentow na terminach
bool solution_init_random(int**);

// PSEUDOLOSOWA LICZBA -- z przedzialu [0..1), o rozkladzie rownomiernym
double drand();

// zwraca numer przedmiotu ktorego terminem jest termin o numerze 'term'
int subject_of_term(int term);

// INICJALIZACJA TABLICY FEROMONU -- poczatkowo wartosciami tej tablicy
// sa preferencje studentow; argument 'n' -- wspolczynnik przez ktory
// macierz zostaje przemnozona (tj. pheromone[stud][term] = n*pref[stud][term])
void pheromone_init(double n);

// AKTUALIZACJA FEROMONU -- na podstawie rozwiazania mrowki 'ant'
// argument 'weight' okresla jak wazne ma byc rozwiazanie tej mrowki
// wartosc ulepszen jest proporcjonalna do pierwiastka kwadratowego
// z wartosci funkcji szczescia danej mrowki
void pheromone_update(Ant& ant, double weight);

// PAROWANIE FEROMONU -- dla kazdej komorki macierzy feromonu:
// pheromone[i][j] *= (1 - rho)
void pheromone_evaporate(double rho);

// ULEPSZANIE FEROMONOWE -- powtarzane kilka razy ('rep_count');
// dla losowego studenta przegladamy wszystkie terminy;
// wybieramy najwieksza wartosci sposrod rand * pheromone[student][termin]
// i przepisujemy studenta na tak znaleziony termin;
// rand to losowa liczba z przedzialu [1 - risk_const/sqrt(gpi + 1), 1]
// dzieki czemu, im dawniej nie bylo poprawy rozwiazania (gpi male), tym
// mrowki zachowuja sie bardziej losowo (szerszy zakres osiagalnych
// rozwiazan
void pheromone_improve(Ant& ant, double risk_const, int rep_count);

// LOKALNE PRZESZUKIWANIE -- pelny przeglad sasiednich rozwiazan;
// przegladamy wszystkich studentow w losowej kolejnosci i szukamy zamian
// terminow, ktore poprawia funkcje szczescia; jesli taka zamiana istnieje,
// to ja wykonujemy
void local_search(Ant& ant);

// OBLICZANIE WSKAZNIKU POSTEPU -- obliczanie GPI; zalezy ono od wzrostu
// wartosci funkcji szczescia najlepszego rozwiazania oraz poprzedniej wartosci
// GPI
void calculate_gpi(double* gpi, int last_best_obj, int new_best_obj, double alpha);

// KOPIUJE PLAN DO MROWKI -- inicjalizuje mrowke
void ant_init(Ant& ant, int** solution);

// MROWKOWY -- wlasciwy algorytm
// ants - zainicjalizowana tablica mrowek
// n - liczba mrowek
// eps - jesli ponizej eps spadnie wartosc GPI, to algorytm sie konczy
// risk_const - jak bardzo losowo moga zachowywac sie mrowki
// gpi_memory - jak wazna przy obliczaniu GPI jest jego wartosc z poprzedniej
//   iteracji
// rho - wsp. parowania feromonu
// dominant_ant_const - o ile wazniejsze jest najlepsze rozwiazanie od pozostalych
//   przy obliczaniu nowej tablicy feromonow
void mrowkowy(Ant* ants, int n, double eps, double risk_const, double gpi_memory, double rho, 
		double dominant_ant_const);
		
// ZAPIS WYJSCIA -- w formacie wewnetrznym, konwertowane
// ./result-int2out.perl
void save_output(int**);

// LOSOWO PERMUTUJE PODANA TABLICE
void randomize_array_order(int* array, int n);

// FUNKCJA CELU
int objective_function(int** plan);

// PRAWDZIWA WARTOSC PREFERENCJI DANEGO TERMINU (taka jak w fcji celu)
// przy zamianie terminu term_out na term_in
int true_pref(int**, int student, int term_in, int term_out);

// STOPIEN ROZPROSZENIA GRUP
// czyli suma ( liczba czlonkow grupy - rozmiar ich najwiekszego skupienia )
int groups_split(int** plan);

// ZMIANA ROZPROSZENIA GRUP
// przy zmianie terminu term_in na term_out, -1, 0, 1
int group_split_diff(int** plan, int stud, int term_in, int term_out);

// LICZBA KOLIZJI W PLANIE STUDENTA stud_id
int has_collision(int** plan, int stud_id);

// LICZBA KOLIZJI Z DANYM TERMINEM (term_in) W PLANIE DANEGO STUDENTA
// przy wyrzuceniu terminu term_out
int term_collides(int** plan, int stud_id, int term_in, int term_out);

#endif
